
#region Imported Namespaces
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Security.Cryptography;
using System.Text;
#endregion

namespace PHSRAG.Utility
{
	#region Class Crypt
	/// <summary>
	/// The Crypt class provides encryption and decryption support using the AES alogorithm (a.k.a. Rijndael).
	/// </summary>
	public class Crypt
	{
		#region Class Variables
        private static RijndaelManaged rijndael = InitializeRijndael();
		private static ASCIIEncoding	textConverter = new ASCIIEncoding();
		#endregion

        static RijndaelManaged InitializeRijndael()
        {
            // The Rijndael system needs to be initialized with a specific KEY and IV values that is common
            // to both encryption and decryption operations. These are bit arrays and are stored in a resource
            // file associated with this assembly.
            RijndaelManaged rijndael = new RijndaelManaged();

            ResourceManager rm = new ResourceManager("PHSRAG.Utility.Crypt", Assembly.GetExecutingAssembly());
            CultureInfo ci = System.Threading.Thread.CurrentThread.CurrentCulture;
            byte[] key = (byte[])rm.GetObject("Key", ci);
            byte[] iv = (byte[])rm.GetObject("IV", ci);
            if ((key == null) || (iv == null))
                throw new Exception("Invalid Key/IV pair");
            rijndael.Key = key;
            rijndael.IV = iv;
            return rijndael;
        }

		#region Public Methods
		/// <summary>
		/// Return the decrypted result of an encrypted data stream.
		/// </summary>
		/// <param name="encryptedData">Stream conatining the encrypted data</param>
		/// <returns>Decrypted string</returns>
		public static string DecryptFromStream(Stream encryptedData)
		{
			byte [] decryptedData = new byte[encryptedData.Length];
			CryptoStream cs = new CryptoStream(encryptedData, rijndael.CreateDecryptor(), CryptoStreamMode.Read);
			cs.Read(decryptedData, 0, decryptedData.Length);

			// Reading from the encrypted stream renders the decrypted stream to contain trailing NULL bytes
			// to render the lengths of the two streams to be equal. Strip such trailing NULL bytes.

			int actualLength = 0;
			for (int index = decryptedData.Length - 1; (index > 0) && (actualLength == 0); --index)
			{
				if (decryptedData[index] != 0)
					actualLength = index + 1;
			}

			return textConverter.GetString(decryptedData, 0, actualLength);
		}

		/// <summary>
		/// Return the decrypted result of an encrypted string which is assumed to be Base64 encoded.
		/// </summary>
		/// <param name="encryptedData">Encrypted string</param>
		/// <returns>Decrypted string</returns>
		public static string DecryptFromString(string encryptedData)
		{
			return DecryptFromStream(new MemoryStream(Convert.FromBase64String(encryptedData)));
		}

		/// <summary>
		/// Encrypt the specified string into a stream.
		/// </summary>
		/// <param name="data">Plain-text string to be encrypted</param>
		/// <returns>MemoryStream containing the encrypted data</returns>
		public static MemoryStream EncryptToStream(string data)
		{
			MemoryStream ms = new MemoryStream();
			CryptoStream cs = new CryptoStream(ms, rijndael.CreateEncryptor(), CryptoStreamMode.Write);

			byte[] bytesToEncrypt = textConverter.GetBytes(data);
			cs.Write(bytesToEncrypt, 0, bytesToEncrypt.Length);
			cs.FlushFinalBlock();

			ms.Seek(0, SeekOrigin.Begin);
			return ms;
		}

		/// <summary>
		/// Encrypt the specified string into a stream.
		/// </summary>
		/// <param name="data">Plain-text string to be encrypted</param>
		/// <returns>string containing the encrypted data</returns>
		public static string EncryptToString(string data)
		{
			// Note that the encryption results in a binary run. Since a string is to be returned, we simply
			// Base64 encode that run and return it. The decryption method expect similar strings.

			return Convert.ToBase64String(EncryptToStream(data).ToArray());
		}
		#endregion
	}
	#endregion
}

// --- EOF ---
